/**
 * @ Author: luoqi
 * @ Create Time: 2024-11-02 10:16
 * @ Modified by: luoqi
 * @ Modified time: 2025-02-17 20:56
 * @ Description:
 */

#include "qfsm.h"

#define QFSM_ENTRY(ptr, type, member)     ((type *)((char *)(ptr) - ((unsigned long) &((type*)0)->member)))
#define QFSM_ITERATOR(node, state_list)                for(node = (state_list)->next; node != (state_list); node = node->next)
#define QFSM_ITERATOR_SAFE(node, safe, state_list)      for(node = (state_list)->next, safe = node->next; node != (state_list); node = safe, safe = node->next)

static uint32_t _id_calc(const char *name)
{
    if(!name) {
        return 0;
    }
    uint32_t hash = 5381;
    int c;
    while ((c = *name++)) {
        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
    }
    return hash;
}

static inline void _list_insert(QFsmList *list, QFsmList *node)
{
    if(!list || !node) {
        return;
    }
    list->next->prev = node;
    node->next = list->next;

    list->next = node;
    node->prev = list;
}

static inline void _list_remove(QFsmList *node)
{
    if(!node) {
        return;
    }
    node->next->prev = node->prev;
    node->prev->next = node->next;

    node->next = node->prev = node;
}

static int _state_isexist(QFsm *fsm, QFsmState *state)
{
    if(!fsm || !state) {
        return -1;
    }
    QFsmState *_state;
    QFsmList *node, *safe;

    QFSM_ITERATOR_SAFE(node, safe, &fsm->state_list)
    {
        _state = QFSM_ENTRY(node, QFsmState, state_node);
        if(_state->id == state->id) {
            return 1;
        } else {
            continue;
        }
    }
    return 0;
}

int qfsm_prepare(QFsm *fsm, const char *start_state_name)
{
    if(!fsm) {
        return -1;
    }
    uint32_t id = _id_calc(start_state_name);

    QFsmState *_state;
    QFsmList *node, *safe;
    
    int found = 0;
    QFSM_ITERATOR_SAFE(node, safe, &fsm->state_list)
    {
        _state = QFSM_ENTRY(node, QFsmState, state_node);
        if(_state->id == id) {
            fsm->current_state = _state;
            found = 1;
            break;
        }
    }
    if(!found) {
        return -1;
    }

    QFSM_ITERATOR_SAFE(node, safe, &fsm->state_list)
    {
        _state = QFSM_ENTRY(node, QFsmState, state_node);
        if(_state->next_state) {
            continue;
        }
        QFsmList *inner_node;
        QFSM_ITERATOR(inner_node, &fsm->state_list)
        {
            QFsmState *candidate = QFSM_ENTRY(inner_node, QFsmState, state_node);
            if(_state->next_state_id == candidate->id) {
                _state->next_state = candidate;
                break;
            }
        }
    }
    return 0;
}

int qfsm_init(QFsm *fsm, uint32_t *param)
{
    if(!fsm) {
        return -1;
    }
    fsm->state_list.next = fsm->state_list.prev = &fsm->state_list;
    fsm->param = param;
    return 0;
}

int qfsm_exec(QFsm *fsm)
{
    if(!fsm) {
        return -1;
    }
    uint32_t ret = fsm->current_state->handle(fsm->param);
    if(ret == QFSM_STATE_CHANGE) {
        if(fsm->hook) {
            fsm->hook(fsm->param);
        }
        if(!fsm->current_state->next_state) {
            return -1;
        }
        fsm->current_state = fsm->current_state->next_state;
    }
    return 0;
}

int qfsm_hook_set(QFsm *fsm, int (*hook)(uint32_t *param))
{
    if(!fsm || !hook) {
        return -1;
    } else {
        fsm->hook = hook;
    }
    return 0;
}

int qfsm_state_attach(QFsm *fsm, QFsmState *state, const char *name, const char *next_state_name, int (*handle)(uint32_t *param))
{
    if(!fsm || !state || !handle) {
        return -1;
    }
    state->name = name;
    state->handle = handle;
    state->id = _id_calc(name);
    state->next_state_id = _id_calc(next_state_name);
    if(_state_isexist(fsm, state)) {
        return -1;
    } else {
        _list_insert(&fsm->state_list, &state->state_node);
    }
    return 0;
}

int qfsm_state_detach(QFsm *fsm, QFsmState *state)
{
    if(!fsm || !state) {
        return -1;
    } else if(_state_isexist(fsm, state)) {
        _list_remove(&state->state_node);
    } else {
        return -1;
    }
    return 0;
}

int qfsm_state_change(QFsm *fsm, const char *state_name)
{
    if(!fsm || !state_name) {
        return -1;
    }
    uint32_t id = _id_calc(state_name);
    QFsmState *_state;
    QFsmList *node, *safe;

    QFSM_ITERATOR_SAFE(node, safe, &fsm->state_list)
    {
        _state = QFSM_ENTRY(node, QFsmState, state_node);
        if(_state->id == id) {
            fsm->current_state = _state;
            return 0;
        } else {
            continue;
        }
    }
    return -1;
}
